/**
 * 
 */


import java.util.ArrayList;
import java.util.List;

/**
 * @author olle
 *
 */

public class Board 
{
	public static int globalInt = 0;
	public int hiddenValue;
	private char[][] board;
	
	private ArrayList<Coord> boxes;
	private ArrayList<Coord> goals;
	private Coord player;
	ArrayList<Coord> deadlocks;
	
	public Board( char[][] board, ArrayList<Coord> boxes, ArrayList<Coord> goals, Coord player )
	{
		this.board = board;
		this.boxes = boxes;
		this.goals = goals;
		this.player = player;
	}
	
	public Board( char[][] board )
	{
		this.board = board;
//		this.boxes = boxes;
//		this.goals = goals;
//		this.player = player;
		
		 boxes = new ArrayList<Coord>();
		 goals = new ArrayList<Coord>();
		 deadlocks = new ArrayList<Coord>();
		
        for ( int x = 0; x < board.length; x++ )
        {
        	for ( int y = 0; y < board[0].length; y++ )
        	{
        		if ( board[x][y] == '@' || board[x][y] == '+' ) //check if player
        		{
        			player = new Coord( x, y );
        		}
        		
        		if ( board[x][y] == '.' || board[x][y] == '+' ) // check if goal
        		{
        			goals.add( new Coord( x, y ) );
        		}
        		
        		if ( board[x][y] == '$' || board[x][y] == '*' ) //check if box
        		{
        			boxes.add( new Coord( x, y ) );
        		}
//        		//Find corner deadlocks
//        		if ( board[x+1][y] == '#' && board[x][y+1] == '#') 
//        		{
//        			deadlocks.add( new Coord( x, y ) );
//        		}
//        		if ( board[x-1][y] == '#' && board[x][y+1] == '#') 
//        		{
//        			deadlocks.add( new Coord( x, y ) );
//        		}
//        		if ( board[x-1][y] == '#' && board[x][y-1] == '#') 
//        		{
//        			deadlocks.add( new Coord( x, y ) );
//        		}
//        		if ( board[x+1][y] == '#' && board[x][y-1] == '#') 
//        		{
//        			deadlocks.add( new Coord( x, y ) );
//        		}
//        		
        	}
        }
		
	}

	
	public void updateBoxes(){
		boxes.clear();
		
		for(int row = 0; row<board.length;row++) {
			for(int col = 0; col < board[0].length; col++) {
				if(board[row][col] == '$' || board[row][col] == '*')
					boxes.add(new Coord(row, col));
			}
		}
	}
	
	public void updatePlayer(Coord playerPos){
		player.x = playerPos.x;
		player.y = playerPos.y;
	}
	
	public Pair<Integer, Integer> getDimension(){
		return new Pair<Integer,Integer>(board.length, board[0].length);
	}
	
	public List<Coord> getLegalMoves() 
	{
		
		ArrayList<Coord> newMoves = new ArrayList<Coord>();
		Coord c = this.getPlayer();
		
		if(board[c.x+1][c.y] == ' ' || board[c.x+1][c.y] == '.' || board[c.x+1][c.y] == '$' || board[c.x+1][c.y] == '*' ) { //can we go right
			if(board[c.x+1][c.y] == '$' || board[c.x+1][c.y] == '*'){
				if(board[c.x+2][c.y] == ' ' || board[c.x+2][c.y] == '.')
					newMoves.add(new Coord(c.x+1, c.y));
			} 
			else { //we get here if empty space
				newMoves.add(new Coord(c.x+1, c.y));
			}
		}if(board[c.x-1][c.y] == ' '|| board[c.x-1][c.y] == '.' || board[c.x-1][c.y] == '$' || board[c.x-1][c.y] == '*' ) { //can we go left
			if(board[c.x-1][c.y] == '$' || board[c.x-1][c.y] == '*'){
				if(board[c.x-2][c.y] == ' ' || board[c.x-2][c.y] == '.')
					newMoves.add(new Coord(c.x-1, c.y));
			} 
			else { //we get here if empty space
				newMoves.add(new Coord(c.x-1, c.y));
			}
		}
		if(board[c.x][c.y+1] == ' '|| board[c.x][c.y+1] == '.' || board[c.x][c.y+1] == '$' || board[c.x][c.y+1] == '*' ) { //can we go down
			if(board[c.x][c.y+1] == '$' || board[c.x][c.y+1] == '*'){
				if(board[c.x][c.y+2] == ' ' || board[c.x][c.y+2] == '.')
					newMoves.add(new Coord(c.x, c.y+1));
			} 
			else { //we get here if empty space
				newMoves.add(new Coord(c.x, c.y+1));
			}
		}
		if(board[c.x][c.y-1] == ' ' || board[c.x][c.y-1] == '.' || board[c.x][c.y-1] == '$' || board[c.x][c.y-1] == '*' ) { //can we go up
			if(board[c.x][c.y-1] == '$' || board[c.x][c.y-1] == '*'){
				if(board[c.x][c.y-2] == ' ' || board[c.x][c.y-2] == '.')
					newMoves.add(new Coord(c.x, c.y-1));
			} 
			else { //we get here if empty space
				newMoves.add(new Coord(c.x, c.y-1));
			}
		}
//		for(Coord _c: newMoves)
//			System.out.println(_c);
		return newMoves;
	}
	

	public char At(int x, int y){
		return board[ x ][ y ];
	}

	public List<Coord> getBoxes() 
	{
		return boxes;
	}

	public List<Coord> getGoals() 
	{
		return goals;
	}

	public Coord getPlayer() 
	{
		return player;
	}
	
	public void setAt( Coord cord, char type )
	{
		board[ cord.x ][ cord.y ] = type;
	}

	public void setAt( int x, int y, char type )
	{
		board[ x ][ y ] = type;
	}
	
	
	public boolean isSolution() {
		return goals.containsAll(boxes);
	}
	
	public Board doMove(Coord dst) throws CloneNotSupportedException 
	{
		Board boardClone = (Board) this.clone();
		
		boardClone.globalInt++;
		boardClone.hiddenValue = boardClone.globalInt;
		
		Coord currentPlayerCord = getPlayer(); //get player position
		char direction = 0;
		
		int diffX = player.x - dst.x;
		int diffY = player.y - dst.y;
		
		
		if(diffX == 0)
			if(diffY > 0) //up
				direction = 'u';
			else //down
				direction = 'd';
		else if(diffY == 0)
			if(diffX>0) //left
				direction = 'l';
			else //right
				direction = 'r';
		
		//moves box (if any)
		if(boardClone.At(dst.x, dst.y) == '$' || boardClone.At(dst.x, dst.y) == '*') { //if box //// ENDRAD
			switch(direction) {
				case 'r':
					if(boardClone.board[ dst.x+1 ][ dst.y ] == '.' )
						boardClone.setAt(dst.x+1, dst.y, '*');
					else
						boardClone.setAt(dst.x+1, dst.y, '$');
					break;
					
				case 'l':
					if(boardClone.board[ dst.x-1 ][ dst.y ] == '.' )
						boardClone.setAt(dst.x-1, dst.y, '*');
					else
						boardClone.setAt(dst.x-1, dst.y, '$');
					break;
				case 'u':
					if(boardClone.board[ dst.x ][ dst.y-1 ] == '.' )
						boardClone.setAt(dst.x, dst.y-1, '*');
					else
						boardClone.setAt(dst.x, dst.y-1, '$');
					break;
				case 'd':
					if(boardClone.board[ dst.x ][ dst.y+1 ] == '.' )
						boardClone.setAt(dst.x, dst.y+1, '*');
					else
						boardClone.setAt(dst.x, dst.y+1, '$');
					break;
			}
		}
		
		//moves player
			if(boardClone.At(dst.x,dst.y) == ' ' || boardClone.At(dst.x,dst.y) == '.'){
				
				if(boardClone.At(dst.x,dst.y) == ' ') {
					boardClone.setAt(dst.x, dst.y, '@');
					
				if(boardClone.At(player.x, player.y) == '@')
					boardClone.setAt(player.x, player.y, ' ');//
				else if(boardClone.At(player.x,player.y) == '+')
					boardClone.setAt(player.x, player.y, '.');
				} else { //if dst is a goal
					boardClone.setAt(dst.x, dst.y, '+');	
					
					if(boardClone.At(player.x, player.y) == '@')
						boardClone.setAt(player.x, player.y, ' ');
					else if(boardClone.At(player.x,player.y) == '+')
						boardClone.setAt(player.x, player.y, '.');
				}
			}else if( boardClone.At(dst.x, dst.y) == '$' || boardClone.At(dst.x, dst.y) == '*'){
				if(boardClone.At(player.x, player.y) == '@') {
					boardClone.setAt(player.x, player.y, ' ');//
					boardClone.setAt(dst.x, dst.y,'@');
				}
				else if(boardClone.At(player.x,player.y) == '+') {
					boardClone.setAt(player.x, player.y, '.');
					boardClone.setAt(dst.x, dst.y, '@');
				}
			}
			
			boardClone.updateBoxes();
			boardClone.updatePlayer(dst);
			
			return boardClone;
	}

	public void printBoard() 
	{
		System.out.println(hiddenValue);
        for ( int y = 0; y < board[0].length; y++ )
        {
        	for ( int x = 0; x < board.length; x++ )
        	{
        		System.out.print( board[ x ][ y ] );
        	}
        	System.out.print( "\n" );
        }
	}

	

	@Override
	public boolean equals(Object obj) {
//		if (this == obj)
//			return true;
//		if (obj == null)
//			return false;
//		if (getClass() != obj.getClass())
//			return false;
//		Board other = (Board) obj;
//		if (boxes == null) {
//			if (other.boxes != null)
//				return false;
//		} else if (!boxes.equals(other.boxes))
//			return false;
//		if (player == null) {
//			if (other.player != null)
//				return false;
//		} else if (!player.equals(other.player))
//			return false;
//	
//		return true;
//	
	
	Board other = (Board)obj;
	
	for(Coord coord:boxes)
		for(Coord coord_other:other.boxes)
			if(!coord.equals(coord_other))
				return false;
	
	if(!this.player.equals(other.player))
		return false;
	
	return true;
	
	}
	
	
	
	
	@Override
	public Board clone()
	{
		
		char[][] cpy = new char[board.length][board[0].length];
		for(int row = 0; row<cpy.length;row++)
			for(int col = 0; col<cpy[0].length; col++)
				cpy[row][col] = board[row][col];
		
		return new Board( 
				cpy, 
				(ArrayList<Coord>) boxes.clone(),
				(ArrayList<Coord>) goals.clone(), 
				(Coord) player.clone() );
	}



}

